/**
 * #info
 *
 *@author   wuwg
 *@createTime   2018/03/20
 *@updateTime   2018/04/23
 *@description  table  component  表格组件
 */

/**
 * #item
 *
 * @type        aty-table
 * @cn          表格
 * @order      130
 * @nameprop    lable
 * @stPlugin    true
 * @stContainer false
 * @children    [aty-table-column]
 * @parents     [aty-panel]
 * @mixins      [
 * ./components/components/data/table/table-body/index.js,
 * ./components/components/data/table/table-footer/index.js,
 * ./components/components/data/table/table-header/index.js,
 * ./components/components/data/table/table-store/index.js
 * ]
 */

import config from '../../../config/config.js'

// tools
import { addResizeListener, removeResizeListener } from '../../../utils/resize-event.js'
import Mousewheel from '../../../directives/mousewheel.js'
import debounce from 'throttle-debounce/debounce'
import { getTableQueryInfo } from './unit/util.js'
import {nullOrUndefinedThen} from "./unit/util";
// mixins
import Locale from '../../../mixins/locale.js'
import Migrating from '../../../mixins/migrating.js'
// components
import TableStore from './table-store/index.js'
import TableLayout from './table-layout/index.js'
import TableBody from './table-body/index.js'
import TableHeader from './table-header/index.js'
import TableFooter from './table-footer/index.js'
import AtyPagination from '../../pagination/pagination/index.vue'
// prefix
const prefixCls = config.prefix + '-checkbox'
let tableIdSeed = 1
// module
export default {
  name: config.prefix + '-table',
  mixins: [Locale, Migrating],
  directives: { Mousewheel },
  components: {
    TableHeader,
    TableFooter,
    TableBody,
    AtyPagination
  },
  props: {
    /**
     * #property
     *
     * @name    pageableData
     * @cn      分页数据
     * @type    array,object
     * @group   分页
     * @value  []
     * @bind    must
     * @description Table单元格中显示的数据。
     *分页时，需要传object类型，格式为：{data:[{id:'1',name:'name1'},{id:'2',name:'name2'}],total:100}。
     */
    pageableData: {
      type: [Array, Object],
      default: function () {
        return []
      }
    },
    /**
     * #property
     *
     * @name    width
     * @cn      高度
     * @type    string,number
     * @group   样式
     * @description Table的宽度，默认为自动宽度。
     */
    width: {
      type: [String, Number]
    },
    /**
     * #property
     *
     * @name    height
     * @cn      高度
     * @type    string,number
     * @group   样式
     * @description Table的高度，默认为自动高度。
     */
    height: {
      type: [String, Number]
    },
    /**
     * #property
     *
     * @name    stripe
     * @cn      斑马纹
     * @type    boolean
     * @group   样式
     * @value   false
     * @description  Table行是否显示为斑马纹。
     */
    stripe: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    border
     * @cn      纵向边框
     * @type    boolean
     * @group   样式
     * @value   false
     * @description  Table是否带有纵向边框。
     */
    border: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    showHeader
     * @cn      显示表头
     * @type    boolean
     * @group   表头
     * @value  true
     * @bind  must
     * @description  是否显示表头。
     */
    showHeader: {
      type: Boolean,
      default: true
    },
    /**
     * #property
     *
     * @name    headerRowClassName
     * @cn      表头行的 className
     * @type    string, function
     * @description   表头行的className的回调方法 Function({ rowIndex })，也可以使用字符串为所有表头行设置一个固定的className。
     */
    headerRowClassName: {
      type: [String, Function]
    },
    /**
     * #property
     *
     * @name    headerCellClassName
     * @cn      表头单元格样式名称
     * @type    string,function
     * @group   表头
     * @description  表头单元格的className的回调方法 Function({ rowIndex, columnIndex, row, column })，也可以使用字符串为所有表头单元格设置一个固定的className。
     */
    headerCellClassName: {
      type: [String, Function]
    },
    /**
     * #property
     *
     * @name    rowClassName
     * @cn      行样式名称
     * @type    string,function
     * @group   样式
     * @description   行的className的回调方法 Function({ row, rowIndex })，也可以使用字符串为所有行设置一个固定的className。
     */
    rowClassName: {
      type: [String, Function]
    },
    /**
     * #property
     *
     * @name    rowStyle
     * @cn      行style
     * @type	object,function
     * @group   样式
     * @bind    must
     * @description   行的style的回调方法，也可以使用一个固定的Object为所有行设置一样的Style。
     */
    rowStyle: {
      type:[Object,Function]
    },
    /**
     * #property
     *
     * @name    cellClassName
     * @cn      单元格样式名称
     * @type    string,function
     * @group   单元格
     * @description   单元格的className的回调方法 Function({ rowIndex, columnIndex, row, column })，也可以使用字符串为所有单元格设置一个固定的className。
     */
    cellClassName: {
      type: [String, Function]
    },
    /**
     * #property
     *
     * @name    rowKey
     * @cn      行数据的 Key
     * @type    string,function
     * @group   数据
     * @description  行数据的Key，用来优化Table的渲染，可以理解为每行记录的主键。
     *类型为 String 时，支持多层访问：user.info.id，但不支持 user.info[0].id，此种情况请使用 Function( row )。
     */
    rowKey: {
      type: [String, Function]
    },
    /**
     * #property
     *
     * @name    highlightCurrentRow
     * @cn      是否要高亮当前行
     * @type    boolean
     * @group   样式
     * @value   false
     * @description  是否要高亮当前行。
     */
    highlightCurrentRow: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    defaultExpandAll
     * @cn      是否默认展开所有行
     * @group   数据
     * @type    boolean
     * @value   false
     * @description      是否默认展开所有行，当Table中存在type="expand" 的 column 时有效。
     */
    defaultExpandAll: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    showSummary
     * @cn      显示合计
     * @type    boolean
     * @group   合计
     * @value   false
     * @description  是否在表尾显示合计行。
     */
    showSummary: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    sumText
     * @cn      合计行第一列的文本
     * @value   合计
     * @type    string
     * @description  合计行第一列的文本。
     */
    sumText: {
      type: String
    },
    /**
     * #property
     *
     * @name    summaryMethod
     * @cn      自定义的合计计算方法
     * @group   合计
     * @type    function
     * @bind    must
     * @description  自定义的合计计算方法  Function({ columns, data })
     */
    summaryMethod: {
      type: Function
    },
    /**
     * #property
     *
     * @name    emptyText
     * @cn      空数据文本
     * @type    string
     * @group   数据
     * @value   暂无数据
     * @description  空数据时显示的文本内容，也可以通过 slot="empty" 设置。
     */
    emptyText: {
      type: String,
      default: '暂无数据'
    },
    /**
     * #property
     *
     * @name    defaultSort
     * @cn      默认的排序列的prop和顺序
     * @type    string
     * @group   排序
     * @description        默认的排序列的prop和order。它的prop属性指定默认的排序的列，order指定默认排序的顺序。
     *使用方式：default-sort="date desc"。
     */
    defaultSort: {
      type: String
    },
    /**
     * #property
     *
     * @name    spanMethod
     * @cn      合并行或列的计算方法
     * @type    function
     * @group   合并
     * @bind    must
     * @description   合并行或列的计算方法 Function({ row, column, rowIndex, columnIndex })
     */
    spanMethod: {
      type: Function
    },
    /**
     * #property
     *
     * @name    selectOnIndeterminate
     * @cn      当仅有部分行被选中时
     * @type    boolean
     * @group   表头
     * @value   true
     * @bind   must
     * @description   在多选表格中，当仅有部分行被选中时，点击表头的多选框时的行为。若为 true，则选中所有行；若为 false，则取消选择所有行。
     */
    selectOnIndeterminate: {
      type: Boolean,
      default: true
    },

    /**
     * #property
     *
     * @name    isSplitPage
     * @cn      是否分页
     * @type    boolean
     * @group   分页
     * @value   true
     * @bind   must
     * @description  是否分页。
     */
    isSplitPage: {
      type: Boolean,
      default: true
    },
    /**
     * #property
     *
     * @name limit
     * @cn   每页数据条数
     * @type number
     * @group  分页
     * @bind must
     * @value 10
     * @description  每页数据条数。
     */
    limit: {
      type: Number,
      default: 10
    },
    /**
     * #property
     *
     * @name limitOpts
     * @cn   每页条数切换的配置
     * @type array
     * @group 分页
     * @bind must
     * @value [10, 15, 20, 30, 40]
     * @description  每页条数切换的配置,[10, 15, 20, 30, 40]。
     */
    limitOpts: {
      type: Array,
      default () {
        return [10, 15, 20, 30, 40]
      }
    },
    /**
     * #property
     *
     * @name limitOptsTransfer
     * @cn 是否transfer分页选项弹层
     * @type boolean
     * @group 分页
     * @value false
     * @description  是否transfer分页选项弹层。
     *设置为true，分页选项弹层将不会被其他元素遮挡。
     */
    limitOptsTransfer: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name  pagebarSize
     * @cn    分页栏的大小
     * @type  list
     * @group  分页
     * @options [small]
     * @description  分页栏的大小,可选值为 [ small] 中的一个，或不填。
     */
    pagebarSize: {
      validator (value) {
        return Artery.isOneOf(value, ['small'])
      }
    },
    /**
     * #property
     *
     * @name  pagebarSimple
     * @cn    是否是简洁版
     * @type  boolean
     * @group  分页
     * @value  false
     * @description  分页栏，是否是简洁版。
     */
    pagebarSimple: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name  showTotal
     * @cn    是否显示总数
     * @type  boolean
     * @group 分页
     * @value  true
     * @bind  must
     * @description  分页栏，是否显示总数。
     */
    showTotal: {
      type: Boolean,
      default: true
    },

    /**
     * #property
     *
     * @name  showElevator
     * @cn    是否显示电梯
     * @type  boolean
     * @group  分页
     * @value  true
     * @bind  must
     * @description  分页栏，是否显示电梯,以便可以快速切换到某一页。
     */
    showElevator: {
      type: Boolean,
      default: true
    },
    /**
     * #property
     *
     * @name  showSizer
     * @cn    是否显示分页数
     * @type  boolean
     * @group  分页
     * @value  true
     * @bind  must
     * @description  分页栏，是否显示分页数，用来改变page-size。
     */
    showSizer: {
      type: Boolean,
      default: true
    },
    /**
     * #property
     *
     * @name  showPagebarOnePage
     * @cn    一页是否显示
     * @type  boolean
     * @group 分页
     * @value  true
     * @bind  must
     * @description  数据不足一页是否显示分页栏。
     */
    showPagebarOnePage: {
      type: Boolean,
      default: true
    },
    /**
     * #property
     *
     * @name  pagebarClassName
     * @cn    样式名称
     * @type  string
     * @group 样式
     * @description  分页栏，自定义的className。
     */
    pagebarClassName: {
      type: String
    },
    /**
     * #property
     *
     * @name    expAll
     * @cn      导出全部
     * @type    boolean
     * @group   导出
     * @value   false
     * @description  是否支持导出全部数据到excel，如果为true则会在表格下方显示导出全部按钮。
     */
    expAll: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    maxDataCount
     * @cn      最大数量
     * @type    number
     * @group   分页
     * @value   0
     * @version  1.0.3
     * @description 不分页情况避免数据量过大请求卡死, 也控制导出全部, 0不限制
     */
    maxDataCount: {
      type: Number,
      default: 0
    },
    /**
     * #property
     *
     * @name    expCurrentPage
     * @cn      导出当前页
     * @type    boolean
     * @group   导出
     * @value   false
     * @description  是否支持导出当前页数据到excel，如果为true则会在表格下方显示导出当前页按钮。
     */
    expCurrentPage: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    showExpProgress
     * @cn      是否展示导出进度
     * @type    boolean
     * @group   导出
     * @value   true
     * @bind    must
     * @description  是否展示导出进度。
     */
    showExpProgress: {
      type: Boolean,
      default: true
    },
    /**
     * #property
     *
     * @name  expReqUrl
     * @cn    导出请求url
     * @type  string
     * @group 导出
     * @description  导出请求url。
     */
    expReqUrl: {
      type: String
    },
    /**
     * #property
     *
     * @name  expFileName
     * @cn    导出文件名称
     * @type  string
     * @group 导出
     * @value 导出文件
     * @description  导出文件的名称，文件拓展名不需要写，都是xls；导出全部时，如果数据量超过阈值，会导出zip。
     */
    expFileName: {
      type: String,
      default: '导出文件'
    },
    /**
     * #property
     *
     * @name  expTitle
     * @cn    导出标题
     * @type  string
     * @group 导出
     * @description  导出文件的标题，注意：这个标题不是文件名称，而是excel内容的标题。
     */
    expTitle: {
      type: String
    },
    /**
     * #property
     *
     * @name  expParams
     * @cn    自定义查询参数
     * @type  object
     * @group  导出
     * @bind  must
     * @description  格式为json字符串，当不通过loadPageData方法加载表格数据时需要绑定此值。
     */
    expParams: {
      type: Object
    },
    /**
     * #property
     *
     * @name  expStyle
     * @cn    设置导出文件的格式
     * @type  string
     * @group 导出
     * @description  可以填写一个json对象设置导出文件样式，内容如下：{ titleColor:'B7B7B7', titleFontColor:'292929', headSize:15, headColor:'9ACD32',
     * headFontColor:'292929', headFontBold:true,headAlign: 'center', dataSize:10, dataColor:'FFFFFF', dataFontColor:'3D3D3D', dataFontBold:false,dataAlign: 'center', columnWidths:[3,10,10] }
     * 说明如下： { titleColor:标题颜色 titleFontColor:标题文字颜色 headSize:列头文字大小, headColor:列头颜色, headFontColor:列头文字颜色,headFontBold:列头加粗,headAlign:列头align,
     * dataSize:内容大小, dataColor:内容背景颜色, dataFontColor:内容字体颜色, dataFontBold:是否加粗, dataAlign: 数据align, columnWidths:每列宽度(单位为字符,最大为255) }
     */
    expStyle: {
      type: String
    },
    context: {}
  },
  // data
  data () {
    const store = new TableStore(this, {
      rowKey: this.rowKey,
      defaultExpandAll: this.defaultExpandAll,
      selectOnIndeterminate: this.selectOnIndeterminate,
      isSplitPage: this.isSplitPage,
      limit: this.limit
    })
    const layout = new TableLayout({
      store,
      table: this,
      showHeader: this.showHeader
    })
    return {
      layout,
      store,
      isHidden: false,
      renderExpanded: null,
      resizeProxyVisible: false,
      resizeState: {
        width: null,
        height: null
      },
      // 是否拥有多级表头
      isGroup: false,
      scrollPosition: 'left',
      // 分页信息
      offset: 0,
      total: 0,
      data: Array.isArray(this.pageableData) ? this.pageableData : this.pageableData.data,
      expProgressDisplay: 'none',
      expProgressPercent: 0
    }
  },
  // watch
  watch: {
    width: {
      immediate: true,
      handler (value) {
        this.layout.setWidth(value)
      }
    },

    height: {
      immediate: true,
      handler (value) {
        this.layout.setHeight(value)
      }
    },

    pageableData: {
      immediate: true,
      handler (value) {
        if (Array.isArray(value)) {
          this.store.commit('setData', value)
        } else {
          this.store.commit('setData', value.data)
          if (value.pageInfo) { // 兼容旧的IpagableData
            // this.current = value.pageInfo.currPageNo;
            this.total = value.pageInfo.rowCount
          } else {
            this.total = value.total
          }

          this.store.commit('setCurrentTotal', this.total)
        }

        if (this.$ready) {
          this.$nextTick(() => {
            this.doLayout()
          })
        }
      }
    },

    limit: {
      immediate: true,
      handler (newVal) {
        if (newVal) {
          this.store.commit('setLimit', newVal)
        }
      }
    }
  },
  computed: {
    wrapClasses () {
      return [
                `${prefixCls}table`,
                'aty-table',
                {
                  'aty-table--striped': this.stripe,
                  'aty-table--border': this.border || this.isGroup,
                  'aty-table--hidden': this.isHidden,
                  'aty-table--group': this.isGroup,
                  'aty-table--scrollable-x': this.layout.scrollX,
                  'aty-table--scrollable-y': this.layout.scrollY,
                  'aty-table--enable-row-hover': !this.store.states.isComplex,
                  'aty-table--enable-row-transition': (this.store.states.data || []).length !== 0 &&
                                                                (this.store.states.data || []).length < 100
                }
      ]
    },

    bodyWrapper () {
      return this.$refs.bodyWrapper
    },

    shouldUpdateHeight () {
      return this.height || this.fixedColumns.length > 0 || this.rightFixedColumns.length > 0
    },

    shouldFixedColumnRowHeight () {
      let rowStyle = this.rowStyle
      if (typeof rowStyle === 'function') {
        rowStyle = rowStyle.call(null)
      }
      let hasFixedColumn = this.store.states.fixedColumns.length || this.store.states.rightFixedColumns.length
      let isSetRowHeight = rowStyle && rowStyle.height
      // 有固定列并且没有设置行高时，应该自动更新固定列表格内容的行高
      return hasFixedColumn && !isSetRowHeight
    },

    selection () {
      return this.store.states.selection
    },

    columns () {
      return this.store.states.columns
    },

    tableData () {
      return this.store.states.data
    },

    fixedColumns () {
      return this.store.states.fixedColumns
    },

    rightFixedColumns () {
      return this.store.states.rightFixedColumns
    },

    bodyWidth () {
      const { bodyWidth, scrollY, gutterWidth } = this.layout
      return bodyWidth ? bodyWidth - (scrollY ? gutterWidth : 0) + 'px' : ''
    },

    fixedBodyWidth() {
      const fixedColumns = this.store.states.fixedColumns
      return fixedColumns.reduce((total, column) => {
        if (!column.isRender) {
          return total
        }
        return total + nullOrUndefinedThen(column.width, nullOrUndefinedThen(column.minWidth, 80))
      }, 0)
    },

    rightFixedBodyWidth() {
      const rightFixedColumns = this.store.states.rightFixedColumns;
      return rightFixedColumns.reduce((total, column) => {
        if (!column.isRender) {
          return total;
        }
        return total + nullOrUndefinedThen(column.width, nullOrUndefinedThen(column.minWidth, 80))
      }, 0)
    },

    bodyHeight () {
      if (this.height) {
        return {
          height: this.layout.bodyHeight ? this.layout.bodyHeight + 'px' : ''
        }
      }
      return {}
    },

    fixedBodyHeight () {
      if (this.height) {
        return {
          height: this.layout.fixedBodyHeight ? this.layout.fixedBodyHeight + 'px' : ''
        }
      }
      return {}
    },

    fixedHeight () {
      return {
        height: this.layout.tableHeight ? this.layout.tableHeight + 'px' : ''
      }
    }
  },
  // methods
  methods: {
    getMigratingConfig () {
      return {
        events: {
          expand: 'expand is renamed to expand-change'
        }
      }
    },

    setCurrentRow (row) {
      this.store.commit('setCurrentRow', row)
    },

    /**
     * #method
     *
     * @name    setCurrentRowByKey
     * @param   rowKey 行key
     * @description  根据key设置当前选中行，须与row-key属性配合使用。
     */
    setCurrentRowByKey (rowKey) {
      this.store.setCurrentRowByKey(rowKey)
    },

    /**
     * #method
     *
     * @name    getCurrentRow
     * @description  获得当前行的数据
     * @returns Object
     */
    getCurrentRow () {
      return this.store.states.currentRow
    },

    toggleRowSelection (row, selected) {
      this.store.toggleRowSelection(row, selected)
      this.store.updateAllSelected()
    },

    /**
     * #method
     *
     * @name    toggleRowSelectionByKey
     * @param   rowKey 行key
     * @param   selected 是否选中
     * @description  根据key来toggle选中行，若指定了selected参数，则是 强制选中/强制取消选中 某一行。须与row-key属性配合使用。
     */
    toggleRowSelectionByKey (rowKey, selected) {
      this.store.toggleRowSelectionByKey(rowKey, selected)
      this.store.updateAllSelected()
    },

    /**
     * #method
     *
     * @name    getSelectedRows
     * @description  获得选中行的数据
     * @returns Array
     */
    getSelectedRows () {
      return this.store.states.selection
    },

    /**
     * #method
     *
     * @name    clearSelection
     * @description  清除所有选中行。
     */
    clearSelection () {
      this.store.clearSelection()
    },

    toggleRowExpansion (row, expanded) {
      this.store.toggleRowExpansion(row, expanded)
    },

    /**
     * #method
     *
     * @name    toggleRowExpansionByKey
     * @param   rowKey 行key
     * @param   expanded 是否展开
     * @description  根据key来toggle展开行，若指定了expanded参数，则是 强制展开/强制取消展开 某一行。须与row-key属性配合使用。
     */
    toggleRowExpansionByKey (rowKey, expanded) {
      this.store.toggleRowExpansionByKey(rowKey, expanded)
    },

    /**
     * #method
     *
     * @name    collapseExpansion
     * @description  收起所有展开行。
     */
    collapseExpansion () {
      this.store.collapseExpansion()
    },

    clearSort () {
      this.store.clearSort()
    },

    handleMouseLeave () {
      this.store.commit('setHoverRow', null)
      if (this.hoverState) this.hoverState = null
    },

    updateScrollY () {
      this.layout.updateScrollY()
    },

    handleFixedMousewheel (event, data) {
      const bodyWrapper = this.bodyWrapper
      if (Math.abs(data.spinY) > 0) {
        const currentScrollTop = bodyWrapper.scrollTop
        if (data.pixelY < 0 && currentScrollTop !== 0) {
          event.preventDefault()
        }
        if (data.pixelY > 0 && bodyWrapper.scrollHeight - bodyWrapper.clientHeight > currentScrollTop) {
          event.preventDefault()
        }
        bodyWrapper.scrollTop += Math.ceil(data.pixelY / 5)
      } else {
        bodyWrapper.scrollLeft += Math.ceil(data.pixelX / 5)
      }
    },

    handleHeaderFooterMousewheel (event, data) {
      const { pixelX, pixelY } = data
      if (Math.abs(pixelX) >= Math.abs(pixelY)) {
        event.preventDefault()
        this.bodyWrapper.scrollLeft += data.pixelX / 5
      }
    },

    bindEvents () {
      const { headerWrapper, footerWrapper } = this.$refs
      const refs = this.$refs
      const self = this

      this.bodyWrapper.addEventListener('scroll', function () {
        if (headerWrapper) headerWrapper.scrollLeft = this.scrollLeft
        if (footerWrapper) footerWrapper.scrollLeft = this.scrollLeft
        if (refs.fixedBodyWrapper) refs.fixedBodyWrapper.scrollTop = this.scrollTop
        if (refs.rightFixedBodyWrapper) refs.rightFixedBodyWrapper.scrollTop = this.scrollTop
        const maxScrollLeftPosition = this.scrollWidth - this.offsetWidth - 1
        const scrollLeft = this.scrollLeft
        if (scrollLeft >= maxScrollLeftPosition) {
          self.scrollPosition = 'right'
        } else if (scrollLeft === 0) {
          self.scrollPosition = 'left'
        } else {
          self.scrollPosition = 'middle'
        }
      })

      addResizeListener(this.$el, this.resizeListener)
    },

    resizeListener () {
      if (!this.$ready) return
      let shouldUpdateLayout = false
      const el = this.$el
      const { width: oldWidth, height: oldHeight } = this.resizeState

      const width = el.offsetWidth
      if (oldWidth !== width) {
        shouldUpdateLayout = true
      }

      const height = el.offsetHeight
      if ((this.height || this.shouldUpdateHeight) && oldHeight !== height) {
        shouldUpdateLayout = true
      }

      if (shouldUpdateLayout) {
        this.resizeState.width = width
        this.resizeState.height = height
        this.doLayout()
      }
    },

    doLayout () {
      this.layout.updateColumnsWidth()
      if (this.shouldUpdateHeight) {
        this.layout.updateElsHeight()
      }
      if (this.shouldFixedColumnRowHeight) {
        this.layout.updateFixedColumnRowHeight()
      }
    },
    handlePageChange (page) {
      this.store.commit('setLimit', page.limit)
      this.store.commit('setOffset', page.offset)
      this.offset = page.offset
      const queryInfo = getTableQueryInfo(this.store.states, this.defaultSort)
      queryInfo.tableId = this.tableId
     this.$emit('load-data', queryInfo)
    },
    /**
     * #method
     *
     * @name    reloadData
     * @param   gotoFirstPage boolean类型，是否回到第一页
     * @description  重新加载列表。reloadData会触发load-data事件。
     */
    reloadData (gotoFirstPage) {
      const copyStates = Object.assign({}, this.store.states)
      const tablePagination = this.$refs.tablePagination
      if (tablePagination) {
        copyStates.limit = tablePagination.currentPageSize
      } else {
        copyStates.limit = this.limit
      }
      copyStates.offset = gotoFirstPage ? 0 : this.offset
      this.offset = gotoFirstPage ? 0 : this.offset

      const queryInfo = getTableQueryInfo(copyStates, this.defaultSort, gotoFirstPage)
      queryInfo.tableId = this.tableId
      this.$emit('load-data', queryInfo)

    },
    getExportInfos (dataFieldInfo, fieldInfo, column, level, childLevel) {
      if (column.exportable) {
        let hasChild = false
        let childCount = 0
        if (column.children) {
          let i = 0
          column.children.forEach(childColumn => {
            if (childColumn.exportable) {
              if (i == 0) {
                childLevel.index++
                i++
                hasChild = true
              }
              this.getExportInfos(dataFieldInfo, fieldInfo, childColumn, level + 1, childLevel)
              childCount += (childColumn.childCount == 0) ? 1 : childColumn.childCount
            }
          })
        }
        column.childCount = childCount
        const field = {
          childLevel: hasChild ? childLevel.index - level : 0,
          childCount: childCount,
          property: column.property,
          label: column.label,
          type: column.type,
          formatter: column.formatterStringify,
        }

        if (field.property || field.type == 'index' || hasChild) {
          if (!fieldInfo[level]) {
            fieldInfo[level] = [field]
          } else {
            fieldInfo[level].push(field)
          }
          if (!hasChild) {
            dataFieldInfo.push(field)
          }
        }
      }
    },
    handleExportData (type) {
      const fieldInfo = []
      const dataFieldInfo = []
      this.store.states._columns.forEach(column => {
        if (column.exportable) {
          this.getExportInfos(dataFieldInfo, fieldInfo, column, 0, { index: 0 })
        }
      })
      if (fieldInfo.length === 0) {
        Artery.message.error('导出列为空')
        return
      }
      const copyStates = Object.assign({}, this.store.states)
      copyStates.offset = this.offset
      const queryInfo = getTableQueryInfo(copyStates, this.defaultSort)
      queryInfo.tableId = this.tableId
      queryInfo.dataFieldInfo = dataFieldInfo
      queryInfo.fieldInfo = fieldInfo
      queryInfo.expFileName = this.expFileName
      queryInfo.expTitle = this.expTitle
      queryInfo.expStyle = this.expStyle
      if (this.spanMethod) {
        queryInfo.spanMethod = this.spanMethodStringify
      }
      queryInfo.splitPage = type === 'current'
      queryInfo.showExpProgress = this.showExpProgress
      queryInfo.maxDataCount = this.maxDataCount
      Artery.exportExcel(this, queryInfo)
    },
    updateAllSelected () {
      this.store.updateAllSelected()
    }
  },

  created () {
    this.tableId = 'aty-table_' + tableIdSeed++
    this.debouncedUpdateLayout = debounce(50, () => this.doLayout())
  },
  beforeMount() {
    const queryInfo = getTableQueryInfo(this.store.states, this.defaultSort)
    queryInfo.tableId = this.tableId
    this.$emit('load-data', queryInfo)
  },
  mounted () {
    this.bindEvents()
    this.store.updateColumns()
    this.doLayout()

    this.resizeState = {
      width: this.$el.offsetWidth,
      height: this.$el.offsetHeight
    }

    this.$ready = true
  },
  destroyed () {
    if (this.resizeListener) removeResizeListener(this.$el, this.resizeListener)
  }

}
